home *** CD-ROM | disk | FTP | other *** search
/ Mac-Source 1994 July / Mac-Source_July_1994.iso / C and C++ / Libraries / GUSI / GUSITCP.cp < prev    next >
Encoding:
Text File  |  1993-12-30  |  22.2 KB  |  888 lines  |  [TEXT/MPS ]

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSITCP.cp        -    TCP Stream Sockets
  4. Author    :    Matthias Neeracher
  5.  
  6.     This file was derived from the socket library by
  7.  
  8.         Charlie Reiman    <creiman@ncsa.uiuc.edu> and
  9.         Tom Milligan    <milligan@madhaus.utcs.utoronto.ca>
  10.  
  11. Started    :    12Aug92                                Language    :    MPW C/C++
  12. Modified    :    08Sep92    MN    A big part should work now                
  13.                 08Jan93    MN    tcp_notify was setting the wrong state to unconnected
  14.                 17Jan93    MN    Be more careful about user interrupts.
  15.                 21Jan93    MN    Simplify and correct code
  16.                 31Jan93    MN    Support for inetd
  17.                 25Aug93    MN    return correct peer address from accept()
  18. Last        :    25Aug93
  19. *********************************************************************/
  20.  
  21. #include "GUSIINET_P.h"
  22.  
  23. /********************** Completion procedures ***********************/
  24.  
  25. #pragma segment GUSIResident
  26.  
  27. pascal void tcp_notify(
  28.     StreamPtr,
  29.     u_short                    eventCode,
  30.     Ptr                        userDataPtr,
  31.     u_short,
  32.     struct ICMPReport *)
  33. {
  34.     TCPSocket *    sock    =    *(TCPSocket **) userDataPtr;
  35.  
  36.     switch (eventCode) {
  37.     case TCPClosing:
  38.         sock->sstate = SOCK_STATE_CLOSING;
  39.         break;
  40.  
  41.     case TCPTerminate:
  42.         sock->sstate = SOCK_STATE_UNCONNECTED;
  43.         break;
  44.     }
  45. }
  46.  
  47. void tcp_connect_done(AnnotatedPB *pb)
  48. {
  49.     TCPSocket *    sock    =    (TCPSocket *) pb->Owner();
  50.     TCPiopb *    tcp    =    pb->TCP();
  51.  
  52.     if (!tcp->ioResult) {
  53.         sock->sa.sin_addr.s_addr     = tcp->csParam.open.localHost;
  54.         sock->sa.sin_port             = tcp->csParam.open.localPort;
  55.         sock->peer.sin_addr.s_addr    = tcp->csParam.open.remoteHost;
  56.         sock->peer.sin_port             = tcp->csParam.open.remotePort;
  57.         sock->sstate                     = SOCK_STATE_CONNECTED;
  58.         sock->asyncerr                 = noErr;
  59.     }
  60. }
  61.  
  62. void tcp_listen_done(AnnotatedPB *pb)
  63. {
  64.     TCPSocket *    sock    =    (TCPSocket *) pb->Owner();
  65.     TCPiopb *    tcp    =    pb->TCP();
  66.  
  67.     switch(tcp->ioResult) {
  68.     case noErr:
  69.         sock->peer.sin_addr.s_addr    = tcp->csParam.open.remoteHost;
  70.         sock->peer.sin_port             = tcp->csParam.open.remotePort;
  71.         sock->sstate                     = SOCK_STATE_LIS_CON;
  72.         sock->asyncerr                 = 0;
  73.         break;
  74.  
  75.     case openFailed:
  76.     case invalidStreamPtr:
  77.     case connectionExists:
  78.     case duplicateSocket:
  79.     case commandTimeout:
  80.     default:
  81.         sock->sstate                     = SOCK_STATE_UNCONNECTED;
  82.         sock->asyncerr                 = tcp->ioResult;
  83.         break;
  84.     }
  85. }
  86.  
  87. void tcp_recv_done(AnnotatedPB *pb)
  88. {
  89.     TCPSocket *        sock    =    (TCPSocket *) pb->Owner();
  90.     TCPiopb *        tcp    =    pb->TCP();
  91.     register    int     readin;
  92.  
  93.     if (!tcp->ioResult) {
  94.         readin = tcp->csParam.receive.rcvBuffLen;
  95.         sock->recvd   = readin;
  96.     }
  97. }
  98.  
  99. void tcp_send_done(AnnotatedPB *pb)
  100. {
  101.     TCPSocket *    sock    =    (TCPSocket *) pb->Owner();
  102.     TCPiopb *    tcp    =    pb->TCP();
  103.  
  104.     switch (tcp->ioResult) {
  105.     case noErr:
  106.         ((wdsEntry *)(tcp->csParam.send.wdsPtr))->length = 0;    /* mark it free */
  107.         break;
  108.  
  109.     case ipNoFragMemErr:
  110.     case connectionClosing:
  111.     case connectionTerminated:
  112.     case connectionDoesntExist:
  113.         sock->sstate     = SOCK_STATE_UNCONNECTED;
  114.         sock->asyncerr = ENOTCONN;
  115.         break;
  116.  
  117.     case ipDontFragErr:
  118.     case invalidStreamPtr:
  119.     case invalidLength:
  120.     case invalidWDS:
  121.     default:
  122.         sock->sstate     = SOCK_STATE_UNCONNECTED;
  123.         sock->asyncerr = tcp->ioResult;
  124.         break;
  125.     }
  126. }
  127.  
  128. #pragma segment GUSIINET
  129.  
  130. /************************ TCPSocket members *************************/
  131.  
  132.  
  133. TCPSocket::TCPSocket()
  134.     :    INETSocket()
  135. {
  136.     TCPiopb    pb;
  137.  
  138.     sstate     = SOCK_STATE_UNCONNECTED;
  139.     self          = new(TCPSocket *);
  140.     *self        = this;
  141.  
  142.     pb.ioCRefNum                         = INETSockets.Driver();
  143.     pb.csCode                             = TCPCreate;
  144.     pb.csParam.create.rcvBuff         = (char *)NewPtr(STREAM_BUFFER_SIZE);
  145.     pb.csParam.create.rcvBuffLen     = STREAM_BUFFER_SIZE;
  146.     pb.csParam.create.notifyProc     = tcp_notify;
  147.     pb.csParam.create.userDataPtr    = Ptr(self);
  148.  
  149.     switch(PBControlSync(ParmBlkPtr(&pb)))
  150.     {
  151.         case noErr:                     break;
  152.         case invalidLength:             GUSI_error(ENOBUFS);     return;
  153.         case invalidBufPtr:             GUSI_error(ENOBUFS);     return;
  154.         case insufficientResources:     GUSI_error(EMFILE);         return;
  155.         default:                             GUSI_error(ENETDOWN);     return;
  156.     }
  157.  
  158.     peer.sin_family         = AF_INET;
  159.     peer.sin_addr.s_addr = 0;
  160.     peer.sin_port             = 0;
  161.  
  162.     bzero(&peer.sin_zero[0], 8);
  163.  
  164.     asyncerr                 = 0;
  165.     stream                     = pb.tcpStream;
  166. }
  167.  
  168. TCPSocket::TCPSocket(StreamPtr stream)
  169.     :    INETSocket(stream)
  170. {
  171.     AppleEvent                theEvent, myReply;
  172.     AEDesc                    theAddress;
  173.     long                        theType = 'inet';
  174.     ProcPtr                    theProc    = (ProcPtr) tcp_notify;
  175.     ProcessSerialNumber    PSN;
  176.  
  177.     self          = new(TCPSocket *);
  178.     *self        = this;
  179.     asyncerr = 0;
  180.  
  181.     GetCurrentProcess(&PSN);
  182.     AECreateDesc(typeApplSignature, (Ptr) &theType, sizeof(theType), &theAddress);
  183.     AECreateAppleEvent('INET', 'TNFY',  &theAddress, kAutoGenerateReturnID, kAnyTransactionID, &theEvent);
  184.     
  185.     AEPutParamPtr(&theEvent, 'STRM', typeLongInteger, (Ptr) &stream, sizeof(stream));
  186.     AEPutParamPtr(&theEvent, 'ASR ', typeLongInteger, (Ptr) &theProc, sizeof(ProcPtr));
  187.     AEPutParamPtr(&theEvent, 'USRP', typeLongInteger, (Ptr) &self, sizeof(long));
  188.     AEPutParamPtr(&theEvent, keyProcessSerialNumber, typeProcessSerialNumber, (Ptr) &PSN, sizeof(ProcessSerialNumber));
  189.     
  190.     AESend(&theEvent, &myReply, kAEWaitReply, kAEHighPriority, 120, nil, nil);
  191.  
  192.     AEDisposeDesc(&myReply);
  193.     AEDisposeDesc(&theEvent);
  194.     AEDisposeDesc(&theAddress);
  195.  
  196.     TCPiopb * pb;
  197.  
  198.     pb                = GetPB();
  199.     pb->csCode     = TCPStatus;
  200.  
  201.     PBControlSync(ParmBlkPtr(pb));
  202.     
  203.     sa.sin_addr.s_addr     =     pb->csParam.status.localHost;
  204.     sa.sin_port                =    pb->csParam.status.localPort;
  205.     peer.sin_addr.s_addr =     pb->csParam.status.remoteHost;
  206.     peer.sin_port            =    pb->csParam.status.remotePort;
  207. }
  208.  
  209. TCPSocket::TCPSocket(TCPSocket * sock)
  210. {
  211.     stream        = sock->stream;
  212.     status        = sock->status;
  213.     nonblocking    = sock->nonblocking;
  214.     recvBuf        = sock->recvBuf;
  215.     recvd            = sock->recvd;
  216.     sa                = sock->sa;
  217.     peer            = sock->peer;
  218.     sstate        = sock->sstate;
  219.     asyncerr        = 0;
  220.     
  221.     // The reason for this strange code is that stream->userData points to
  222.     // sock.self and cannot be changed while the stream is alive
  223.     
  224.     self            = sock->self;
  225.     *self            = this;
  226.     sock->self    = new(TCPSocket *);
  227.     *sock->self    = sock;
  228. }
  229.  
  230. TCPSocket::~TCPSocket()
  231. {
  232.     TCPiopb *    pb;
  233.  
  234.     do {
  235.         pb                    = GetPB();
  236.         pb->csCode         = TCPStatus;
  237.  
  238.         PBControlSync(ParmBlkPtr(pb));
  239.  
  240.         SAFESPIN(false, SP_MISC, 0);
  241.     } while (!errno && pb->csParam.status.amtUnackedData > 0);
  242.  
  243.     pb                                                = GetPB();
  244.     pb->ioCompletion                            = nil;
  245.     pb->csCode                                     = TCPClose;
  246.     pb->csParam.close.validityFlags         = timeoutValue | timeoutAction;
  247.     pb->csParam.close.ulpTimeoutValue     = 60 /* seconds */;
  248.     pb->csParam.close.ulpTimeoutAction     = 1 /* 1:abort 0:report */;
  249.  
  250.     switch (PBControlAsync(ParmBlkPtr(pb)))
  251.     {
  252.         case noErr:
  253.         case connectionClosing:
  254.             break;
  255.         case connectionDoesntExist:
  256.         case connectionTerminated:
  257.             break;
  258.         case invalidStreamPtr:
  259.         default:
  260.             return;
  261.     }
  262.  
  263.     {
  264.         rdsEntry    rdsarray[TCP_MAX_WDS+1];
  265.         int        passcount;
  266.         const int maxpass =4;
  267.  
  268.         pb                    =    GetPB();
  269.  
  270.         for (passcount=0; passcount<maxpass; passcount++) {
  271.             pb->csCode                                             = TCPNoCopyRcv;
  272.             pb->csParam.receive.commandTimeoutValue    = 1; /* seconds, 0 = blocking */
  273.             pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  274.             pb->csParam.receive.rdsLength                 = TCP_MAX_WDS;
  275.  
  276.             if (PBControlSync(ParmBlkPtr(pb)))
  277.                 break;
  278.  
  279.             pb->csCode                                             = TCPRcvBfrReturn;
  280.             pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  281.  
  282.             PBControlSync(ParmBlkPtr(pb));
  283.  
  284.             SAFESPIN(false, SP_MISC, 0);
  285.             
  286.             if (errno)
  287.                 break;
  288.         }
  289.  
  290.         if (passcount == maxpass) {        /* remote side isn't being nice */
  291.             /* then try again */
  292.  
  293.             PBControlSync(ParmBlkPtr(pb));
  294.  
  295.             for (passcount=0; passcount<maxpass; passcount++) {
  296.                 pb->csCode                                             = TCPNoCopyRcv;
  297.                 pb->csParam.receive.commandTimeoutValue    = 1; /* seconds, 0 = blocking */
  298.                 pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  299.                 pb->csParam.receive.rdsLength                 = TCP_MAX_WDS;
  300.  
  301.                 if (PBControlSync(ParmBlkPtr(pb)))
  302.                     break;
  303.  
  304.                 pb->csCode                                             = TCPRcvBfrReturn;
  305.                 pb->csParam.receive.rdsPtr                     = (Ptr)rdsarray;
  306.  
  307.                 PBControlSync(ParmBlkPtr(pb));
  308.  
  309.                 SAFESPIN(false, SP_MISC, 0);
  310.                 
  311.                 if (errno)
  312.                     break;
  313.             }
  314.         }
  315.     }
  316.  
  317.     /* destroy the stream */
  318.     pb->csCode     = TCPRelease;
  319.  
  320.     if (PBControlSync(ParmBlkPtr(pb)))
  321.         return;
  322.  
  323.     DisposPtr(pb->csParam.create.rcvBuff); /* there is no release pb */
  324.     
  325.     delete self;
  326. }
  327.  
  328. TCPiopb * TCPSocket::GetPB()
  329. {
  330.     AnnotatedPB *    pb        =    INETSockets.GetPB();
  331.     pb->TCP()->ioCRefNum =    INETSockets.Driver();
  332.     pb->TCP()->tcpStream    =    stream;
  333.  
  334.     pb->SetOwner(this);
  335.  
  336.     return pb->TCP();
  337. }
  338.  
  339. u_long TCPSocket::Available()
  340. {
  341.     TCPiopb * pb;
  342.  
  343.     pb                = GetPB();
  344.     pb->csCode     = TCPStatus;
  345.  
  346.     PBControlSync(ParmBlkPtr(pb));
  347.  
  348.     return pb->csParam.status.amtUnreadData;
  349. }
  350.  
  351. /*
  352.  *    connect - initiate a connection on a MacTCP socket
  353.  *
  354.  *        This call attempts to make a
  355.  *        connection to another socket. The other socket is specified
  356.  *        by an internet address and port.
  357.  *
  358.  *        TCP sockets may successfully connect() only once;
  359.  *
  360.  *        If the connection or binding succeeds, then 0 is returned.
  361.  *        Otherwise a -1 is returned, and a more specific error code
  362.  *        is stored in errno.
  363.  *
  364.  *        EAFNOSUPPORT        The address family in addr is not AF_INET.
  365.  *
  366.  *        EHOSTUNREACH        The TCP connection came up half-way and
  367.  *                          then failed.
  368.  */
  369.  
  370. int TCPSocket::connect(void * address, int addrlen)
  371. {
  372.     OSErr                        err;
  373.     struct sockaddr_in *    addr    =    (struct sockaddr_in *) address;
  374.     TCPiopb    *                pb;
  375.  
  376.     if (addrlen != sizeof(struct sockaddr_in))
  377.         return GUSI_error(EINVAL);
  378.  
  379.     if (addr->sin_family != AF_INET)
  380.         return GUSI_error(EAFNOSUPPORT);
  381.  
  382.     /* Make sure this socket can connect. */
  383.     if (sstate == SOCK_STATE_CONNECTING)
  384.         return GUSI_error(EALREADY);
  385.     if (sstate != SOCK_STATE_UNCONNECTED)
  386.         return GUSI_error(EISCONN);
  387.  
  388.     sstate = SOCK_STATE_CONNECTING;
  389.  
  390.     pb                                                    = GetPB();
  391.     pb->ioCompletion                                = TCPIOCompletionProc(tcp_connect_done);
  392.     pb->csCode                                         = TCPActiveOpen;
  393.     pb->csParam.open.validityFlags             = timeoutValue | timeoutAction;
  394.     pb->csParam.open.ulpTimeoutValue         = 60 /* seconds */;
  395.     pb->csParam.open.ulpTimeoutAction         = 1 /* 1:abort 0:report */;
  396.     pb->csParam.open.commandTimeoutValue     = 0;
  397.     pb->csParam.open.remoteHost                 = addr->sin_addr.s_addr;
  398.     pb->csParam.open.remotePort                 = addr->sin_port;
  399.     pb->csParam.open.localHost                 = 0;
  400.     pb->csParam.open.localPort                 = sa.sin_port;
  401.     pb->csParam.open.dontFrag                     = 0;
  402.     pb->csParam.open.timeToLive                 = 0;
  403.     pb->csParam.open.security                     = 0;
  404.     pb->csParam.open.optionCnt                 = 0;
  405.  
  406.     if (err = PBControlAsync(ParmBlkPtr(pb)))
  407.     {
  408.         sstate = SOCK_STATE_UNCONNECTED;
  409.         return TCP_error(err);
  410.     }
  411.  
  412.     if (nonblocking)
  413.         return GUSI_error(EINPROGRESS);
  414.  
  415.     /* sync connect - spin till TCPActiveOpen completes */
  416.  
  417.     SAFESPIN(pb->ioResult==inProgress, SP_MISC, 0);
  418.  
  419.     if (errno || pb->ioResult) {
  420.         sstate = SOCK_STATE_UNCONNECTED;
  421.         
  422.         if (errno)
  423.             return -1;
  424.         else
  425.             return TCP_error(pb->ioResult);
  426.     } else
  427.         return 0;
  428. }
  429.  
  430. int TCPSocket::listen(int)
  431. {
  432.     OSErr            err;
  433.     TCPiopb *    pb;
  434.  
  435.     if (sstate != SOCK_STATE_UNCONNECTED)
  436.         return GUSI_error(EISCONN);
  437.  
  438.     sstate                                             = SOCK_STATE_LISTENING;
  439.     pb                                                    = GetPB();
  440.     pb->ioCRefNum                                     = INETSockets.Driver();
  441.     pb->ioCompletion                                = TCPIOCompletionProc(tcp_listen_done);
  442.     pb->csCode                                         = TCPPassiveOpen;
  443.     pb->csParam.open.validityFlags             = timeoutValue | timeoutAction;
  444.     pb->csParam.open.ulpTimeoutValue         = 255 /* seconds */;
  445.     pb->csParam.open.ulpTimeoutAction         = 0 /* 1:abort 0:report */;
  446.     pb->csParam.open.commandTimeoutValue     = 0 /* infinity */;
  447.     pb->csParam.open.remoteHost                 = 0;
  448.     pb->csParam.open.remotePort                 = 0;
  449.     pb->csParam.open.localHost                 = 0;
  450.     pb->csParam.open.localPort                 = sa.sin_port;
  451.     pb->csParam.open.dontFrag                     = 0;
  452.     pb->csParam.open.timeToLive                 = 0;
  453.     pb->csParam.open.security                     = 0;
  454.     pb->csParam.open.optionCnt                 = 0;
  455.  
  456.     if (err = PBControlAsync(ParmBlkPtr(pb))) {
  457.         sstate = SOCK_STATE_UNCONNECTED;
  458.         
  459.         return TCP_error(err);
  460.     }
  461.     
  462.     SAFESPIN(!pb->csParam.open.localPort, SP_MISC, 0);
  463.         
  464.     if (errno) {
  465.         sstate = SOCK_STATE_UNCONNECTED;
  466.         
  467.         return -1;
  468.     }
  469.  
  470.     sa.sin_addr.s_addr     = pb->csParam.open.localHost;
  471.     sa.sin_port             = pb->csParam.open.localPort;
  472.  
  473.     return 0;
  474. }
  475.  
  476. Socket * TCPSocket::accept(void *from, int *fromlen)
  477. {
  478.     TCPSocket *        sock;
  479.     TCPiopb *        pb;
  480.  
  481.     if (sstate == SOCK_STATE_UNCONNECTED)
  482.         if (asyncerr) {
  483.             TCP_error(asyncerr);
  484.             asyncerr = 0;
  485.  
  486.             return nil;
  487.         } else
  488.             return (Socket *) GUSI_error_nil(ENOTCONN);
  489.  
  490.     if (sstate != SOCK_STATE_LISTENING && sstate != SOCK_STATE_LIS_CON)
  491.         return (Socket *) GUSI_error_nil(ENOTCONN);
  492.  
  493.     if (sstate == SOCK_STATE_LISTENING) {
  494.         if (nonblocking)
  495.             return (Socket *) GUSI_error_nil(EWOULDBLOCK);
  496.  
  497.         /*    Spin till sock_tcp_listen_done runs. */
  498.         SPINP(sstate == SOCK_STATE_LISTENING, SP_MISC, 0);
  499.  
  500.         /* got notification - was it success? */
  501.         if (sstate != SOCK_STATE_LIS_CON) {
  502.             (void) TCP_error(asyncerr);
  503.             asyncerr = 0;
  504.             return nil;
  505.         }
  506.     }
  507.  
  508.     /*
  509.      * Have connection.  Duplicate this socket.  The client gets the connection
  510.      * on the new socket and I create a new stream on the old socket and put it
  511.      * in listen state.
  512.      */
  513.     sstate     = SOCK_STATE_CONNECTED;
  514.     sock        = new TCPSocket(this);
  515.  
  516.     if (!sock)
  517.     {
  518.         /*    Abort the incoming connection. */
  519.         pb                 = GetPB();
  520.         pb->csCode         = TCPAbort;
  521.  
  522.         PBControlSync(ParmBlkPtr(pb));
  523.  
  524.         sstate = SOCK_STATE_UNCONNECTED;
  525.  
  526.         /* try and put the socket back in listen mode */
  527.         if (listen(5) < 0)
  528.         {
  529.             sstate = SOCK_STATE_UNCONNECTED;
  530.             return nil;        /* errno already set */
  531.         }
  532.         return (Socket *) GUSI_error_nil(ENOMEM);
  533.     }
  534.  
  535.     /* Create a new MacTCP stream on the old socket and put it into */
  536.     /* listen state to accept more connections. */
  537.     sstate = SOCK_STATE_UNCONNECTED;
  538.  
  539.     pb                                            = GetPB();
  540.     pb->csCode                                 = TCPCreate;
  541.     pb->csParam.create.rcvBuff         = (char *)NewPtr(STREAM_BUFFER_SIZE);
  542.     pb->csParam.create.rcvBuffLen     = STREAM_BUFFER_SIZE;
  543.     pb->csParam.create.notifyProc     = tcp_notify;
  544.     pb->csParam.create.userDataPtr    = Ptr(self);
  545.  
  546.     switch(PBControlSync(ParmBlkPtr(pb)))
  547.     {
  548.         case noErr:                     break;
  549.         case invalidLength:             return (Socket *) GUSI_error_nil(ENOBUFS);
  550.         case invalidBufPtr:             return (Socket *) GUSI_error_nil(ENOBUFS);
  551.         case insufficientResources:     return (Socket *) GUSI_error_nil(EMFILE);
  552.         default:                             return (Socket *) GUSI_error_nil(ENETDOWN);
  553.     }
  554.  
  555.     peer.sin_family         = AF_INET;
  556.     peer.sin_addr.s_addr = 0;
  557.     peer.sin_port             = 0;
  558.  
  559.     bzero(&peer.sin_zero[0], 8);
  560.  
  561.     asyncerr                 = 0;
  562.     stream                     = pb->tcpStream;
  563.  
  564.     if (listen(5) < 0) {
  565.         /* nothing to listen on */
  566.         sstate = SOCK_STATE_UNCONNECTED;
  567.  
  568.         /* kill the incoming connection */
  569.         pb                    = sock->GetPB();
  570.         pb->csCode        = TCPRelease;
  571.  
  572.         if (!PBControlSync(ParmBlkPtr(pb)))
  573.             DisposPtr(pb->csParam.create.rcvBuff); /* there is no release pb */
  574.  
  575.         return nil; /* errno set */
  576.     }
  577.  
  578.     /* return address of partner */
  579.     memcpy(from, &sock->peer, min(*fromlen, sizeof(struct sockaddr_in)));
  580.  
  581.     return sock;
  582. }
  583.  
  584. /*
  585.  *    TCPSocket::recvfrom(s, buffer, buflen, flags, from, fromlen)
  586.  *
  587.  *        recvfrom() attempts to receive a message (ie a datagram)
  588.  *        on the socket s.
  589.  *
  590.  *        from returns the address of the socket which sent the message.
  591.  *        fromlen is the usual value-result length parameter.
  592.  *
  593.  *        Typically, read() is used with a TCP stream and recv() with
  594.  *        UDP where the idea of a message makes more sense. But in fact,
  595.  *        read() and recv() are equivalent.
  596.  *
  597.  *        Regardless of non-blocking status, if less data is available
  598.  *        than has been requested, only that much data is returned.
  599.  *
  600.  *        If the socket is marked for non-blocking I/O, and the socket
  601.  *        is empty, the operation will fail with the error EWOULDBLOCK.
  602.  *        Otherwise, the operation will block until data is available
  603.  *        or an error occurs.
  604.  *
  605.  *        A return value of zero indicates that the stream has been
  606.  *        closed and all data has already been read. ie. end-of-file.
  607.  *
  608.  *        Flags is ignored.
  609.  *
  610.  *        If successful, the number of bytes actually received is
  611.  *        returned. Otherwise, a -1 is returned and the global variable
  612.  *        errno is set to indicate the error.
  613.  *
  614.  *        ESHUTDOWN    The socket has been shutdown for receive operations.
  615.  */
  616.  
  617. int TCPSocket::recvfrom(void * buffer, int buflen, int, void * from, int *)
  618. {
  619.     TCPiopb    *    pb;
  620.     u_long        dataavail;
  621.  
  622.     if (from)
  623.         return GUSI_error(EOPNOTSUPP);
  624.     if (status & SOCK_STATUS_NOREAD)
  625.         return GUSI_error(ESHUTDOWN);
  626.  
  627.     /* socket hasn't finished connecting yet */
  628.     if (sstate == SOCK_STATE_CONNECTING)
  629.     {
  630.         if (nonblocking)
  631.             return GUSI_error(EWOULDBLOCK);
  632.  
  633.         /* async connect and sync recv? */
  634.  
  635.         SPIN(sstate == SOCK_STATE_CONNECTING,SP_MISC,0);
  636.     }
  637.  
  638.     /* socket is not connected */
  639.     if (!(sstate == SOCK_STATE_CONNECTED))
  640.     {
  641.         /* see if the connect died (pretty poor test) */
  642.         if (sstate == SOCK_STATE_UNCONNECTED && asyncerr != 0 && asyncerr != 1)
  643.         {
  644.             (void) TCP_error(asyncerr);
  645.             asyncerr = 0;
  646.             return -1;
  647.         }
  648.  
  649.         /* I guess he just forgot */
  650.         return GUSI_error(ENOTCONN);
  651.     }
  652.  
  653.     dataavail = Available();
  654.  
  655.     if (nonblocking && !dataavail)
  656.         return GUSI_error(EWOULDBLOCK);
  657.         
  658.     recvBuf    = (char *) buffer;
  659.     recvd        = 0;
  660.     asyncerr    = inProgress;
  661.  
  662.     pb                                                     = GetPB();
  663.     pb->ioCompletion                                    = TCPIOCompletionProc(tcp_recv_done);
  664.     pb->csCode                                             = TCPRcv;
  665.     pb->csParam.receive.commandTimeoutValue     = 0; /* seconds, 0 = blocking */
  666.     pb->csParam.receive.rcvBuff                     = recvBuf;
  667.     pb->csParam.receive.rcvBuffLen                 = min(buflen,TCP_MAX_MSG);
  668.  
  669.     PBControlAsync(ParmBlkPtr(pb));
  670.  
  671.     /* This is potentially dangerous, as there doesn't seem to be a way to
  672.         stop the receive call on an user abort.
  673.     */
  674.     SPIN(pb->ioResult==inProgress, SP_STREAM_READ, buflen);
  675.  
  676.     if (pb->ioResult == commandTimeout)
  677.         pb->ioResult = noErr;
  678.  
  679.     switch(pb->ioResult)
  680.     {
  681.         case noErr:
  682.             asyncerr = noErr;
  683.  
  684.             return recvd;
  685.  
  686.         case connectionClosing:
  687.             return recvd;
  688.  
  689.         case connectionTerminated:
  690.             /* The connection is aborted. */
  691.             sstate = SOCK_STATE_UNCONNECTED;
  692.  
  693.             return GUSI_error(ENOTCONN);
  694.  
  695.         case commandTimeout: /* this one should be caught by sock_tcp_recv_done */
  696.         case connectionDoesntExist:
  697.         case invalidStreamPtr:
  698.         case invalidLength:
  699.         case invalidBufPtr:
  700.         default:
  701.             return TCP_error(pb->ioResult);
  702.     }
  703. }
  704.  
  705. /*
  706.  *    TCPSocket::sendto(s, buffer, buflen, flags, to, tolen)
  707.  *
  708.  *        sendto() is used to transmit a message to another
  709.  *        socket on the socket s.
  710.  *
  711.  *        Typically, write() is used with a TCP stream and send() with
  712.  *        UDP where the idea of a message makes more sense. But in fact,
  713.  *        write() and send() are equivalent.
  714.  *
  715.  *        Write() and send() operations are not considered complete
  716.  *        until all data has been sent and acknowledged.
  717.  *
  718.  *        If a socket is marked for non-blocking I/O, the operation
  719.  *        will return an 'error' of EINPROGRESS.
  720.  *
  721.  *        If the socket is not marked for non-blocking I/O, the write will
  722.  *        block until space becomes available.
  723.  *
  724.  *        write() and send() may be used only when the socket is in a connected
  725.  *        state, sendto() may be used at any time.
  726.  *
  727.  *        Flags is ignored.
  728.  *
  729.  *        These calls return the number of bytes sent, or -1 if an error
  730.  *        occurred.
  731.  *
  732.  *        EINVAL              The sum of the iov_len values in the iov array was
  733.  *                                greater than 65535 (TCP) or 65507 (UDP) or there
  734.  *                      were too many entries in the array (16 for TCP or
  735.  *                      6 for UDP).
  736.  *
  737.  *        ESHUTDOWN        The socket has been shutdown for send operations.
  738.  *
  739.  *        EMSGSIZE         The message is too big to send in one datagram. (UDP)
  740.  *
  741.  *        ENOBUFS          The transmit queue is full. (UDP)
  742.  */
  743.  
  744. int TCPSocket::sendto(void * buffer, int count, int flags, void * to, int)
  745. {
  746.     int            bytes,towrite;
  747.     miniwds *    thiswds;
  748.     short            wdsnum;
  749.     TCPiopb *    pb;
  750.     miniwds        wdsarray[TCP_MAX_WDS];
  751.  
  752.     if (status & SOCK_STATUS_NOWRITE)
  753.         return GUSI_error(ESHUTDOWN);
  754.  
  755.     if (to != NULL) /* sendto */
  756.         return GUSI_error(EOPNOTSUPP);
  757.     if (sstate != SOCK_STATE_CONNECTED && sstate != SOCK_STATE_CONNECTING)
  758.         return GUSI_error(ENOTCONN);
  759.  
  760.     /* socket hasn't finished connecting yet */
  761.     if (sstate == SOCK_STATE_CONNECTING) {
  762.         if (nonblocking)
  763.             return GUSI_error(EALREADY);
  764.  
  765.         /* async connect and sync send? */
  766.         SPIN(sstate == SOCK_STATE_CONNECTING, SP_MISC, 0);
  767.     }
  768.  
  769.     /* socket is not connected */
  770.     if (!(sstate == SOCK_STATE_CONNECTED)) {
  771.         /* see if a previous operation failed */
  772.         if (sstate == SOCK_STATE_UNCONNECTED && asyncerr != 0) {
  773.             (void) TCP_error(asyncerr);
  774.             asyncerr = 0;
  775.             return -1;
  776.         }
  777.  
  778.         /* I guess he just forgot */
  779.         return GUSI_error(ENOTCONN);
  780.     }
  781.  
  782.     pb                    = GetPB();
  783.     pb->csCode         = TCPStatus;
  784.  
  785.     if (PBControlSync(ParmBlkPtr(pb)))
  786.         bytes = 0;
  787.     else {
  788.         bytes = pb->csParam.status.sendWindow - pb->csParam.status.amtUnackedData;
  789.  
  790.         if (bytes < 0)
  791.             bytes = 0;
  792.     }
  793.  
  794.     if (bytes < count && nonblocking)
  795.         return GUSI_error(EWOULDBLOCK);
  796.  
  797.     bytes    =    count;                                                /* save count before we nuke it */
  798.     memset(wdsarray, 0, TCP_MAX_WDS*sizeof(miniwds));    /* clear up terminus and mark empty */
  799.     thiswds = wdsarray;
  800.     wdsnum = 0;
  801.  
  802.     while (count > 0) {
  803.         /* make sure the thing that just finished worked ok */
  804.         if (asyncerr) {
  805.             (void) GUSI_error(asyncerr);
  806.             asyncerr = 0;
  807.             return -1;
  808.         }
  809.  
  810.         towrite=min(count,TCP_MAX_MSG);
  811.  
  812.         /* find a clean wds */
  813.  
  814.         while (thiswds->length != 0) {
  815.             wdsnum = (short)((wdsnum+1)%TCP_MAX_WDS); /* generates compiler warning w/o short - why? */
  816.             if (wdsnum)
  817.                 thiswds++;
  818.             else
  819.                 thiswds = wdsarray;
  820.             SPIN(false, SP_STREAM_WRITE, count);    /* spin once */
  821.         }
  822.  
  823.         /* find a clean pb */
  824.  
  825.         thiswds->length                            = (short)towrite;
  826.         thiswds->ptr                                = (char *) buffer;
  827.         pb                                                = GetPB();
  828.         pb->ioCompletion                            = TCPIOCompletionProc(tcp_send_done);
  829.         pb->csCode                                     = TCPSend;
  830.         pb->csParam.send.validityFlags         = timeoutValue | timeoutAction;
  831.         pb->csParam.send.ulpTimeoutValue     = 60 /* seconds */;
  832.         pb->csParam.send.ulpTimeoutAction     = 1 /* 0:abort 1:report */;
  833.         pb->csParam.send.pushFlag                 = count <= TCP_MAX_MSG;
  834.         pb->csParam.send.urgentFlag             = flags & MSG_OOB;
  835.         pb->csParam.send.wdsPtr                 = (Ptr)thiswds;
  836.         pb->csParam.send.sendFree                 = 0;
  837.         pb->csParam.send.sendLength             = 0;
  838.  
  839.         PBControlAsync(ParmBlkPtr(pb));
  840.  
  841.         SPIN(false, SP_STREAM_WRITE, count);
  842.         count     -= towrite;
  843.         buffer    = (char *) buffer + towrite;
  844.     }
  845.  
  846.     SPIN(pb->ioResult == inProgress, SP_STREAM_WRITE, 0);
  847.  
  848.     if (!pb->ioResult)
  849.         return(bytes);
  850.     else
  851.         return TCP_error(pb->ioResult);
  852. }
  853.  
  854. int TCPSocket::select(Boolean * canRead, Boolean * canWrite, Boolean *)
  855. {
  856.     int    goodies     =     0;
  857.  
  858.     if (canRead)
  859.         switch (sstate) {
  860.         case SOCK_STATE_LIS_CON:
  861.             *canRead    = true;
  862.             ++goodies;
  863.             break;
  864.         case SOCK_STATE_CONNECTED:
  865.             if (Available()) {
  866.                 *canRead    = true;
  867.                 ++goodies;
  868.             }
  869.             break;
  870.         case SOCK_STATE_UNCONNECTED:
  871.         case SOCK_STATE_CLOSING:
  872.             *canRead    = true;
  873.             ++goodies;
  874.             break;
  875.         }
  876.  
  877.     if (canWrite)
  878.         switch (sstate) {
  879.         case SOCK_STATE_CONNECTING:
  880.             break;
  881.         default:
  882.             *canWrite = true;
  883.             ++goodies;
  884.         }
  885.  
  886.     return goodies;
  887. }
  888.